package com.yzh.springboot_rbac.service.impl;


import com.yzh.springboot_rbac.entity.DictData;
import com.yzh.springboot_rbac.entity.DictType;
import com.yzh.springboot_rbac.exception.ServiceException;
import com.yzh.springboot_rbac.mapper.DictDataMapper;
import com.yzh.springboot_rbac.mapper.DictTypeMapper;
import com.yzh.springboot_rbac.service.DictTypeService;
import com.yzh.springboot_rbac.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class DictTypeServiceImpl implements DictTypeService {
    @Autowired
    private DictTypeMapper dictTypeMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private DictDataMapper dictDataMapper;
    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 项目启动时，初始化字典到缓存
     */
    @PostConstruct
    public void init() {
        loadingDictCache();
    }


    /**
     * 1.添加字典类型：
     * - 先往数据库里面添加
     * - 再往redis里面添加键，其他值不用添加
     */

    /*1.添加字典类型*/
    @Override
    public int insertDictType(DictType dict) {
        int row = dictTypeMapper.insertDictType(dict);
        if (row > 0) {
            redisUtil.set(dict.getDictType(), null);
        }
        return row;
    }


    /**
     * 查询系列系列相关方法
     */
    /*1.查询所有字典类型*/
    @Override
    public List<DictType> selectDictTypeAll() {
        return dictTypeMapper.selectDictTypeAll();
    }



    /*2.通过条件查询所有字典*/
    @Override
    public List<DictType> selectDictTypeByConditon(DictType dictType) {
        List<DictType> dictTypeList = dictTypeMapper.selectDictTypeByConditon(dictType);
        return dictTypeList;
    }

    /**
     * 修改字典类型：
     * 注意这个流程：
     * - 1.根据传入的dict对象的id，利用查询字典通过id的方式得到旧的字典类型对象。
     * - 2.把旧对象的dictType字段和修改过的新对象的dictType字典传给DictData的更新字典数据类型的方法updateDictDatType
     * - 3.之后使用更新字典类型在mysql中更新字典类型
     * - 4.mysql中更新完后，调用dictData的查询字典数据通过类型，把数据放到redis中。
     */

    /*辅助方法：selectDictTypeById*/
    @Override
    public DictType selectDictTypeById(Long dictId) {
        return dictTypeMapper.selectDictTypeById(dictId);
    }

    /*1.修改字典类型*/
    @Override
    @Transactional
    public int updateDictType(DictType dictType) {
        /*1.得到旧对象，用辅助方法selectDictTypeById*/
        DictType oldDictType = dictTypeMapper.selectDictTypeById(dictType.getDictId());
        /*2.旧的字典类型和新的字典类型同步更新字典数据表*/
        dictDataMapper.updateDictDataType(oldDictType.getDictType(), dictType.getDictType());

        /*3.更新数据库里面的字典类型表*/
        int row = dictTypeMapper.updateDictType(dictType);
        if (row > 0) {
            /*4.更新redis里面的缓存的字典数据*/
            List<DictData> dictDataList = dictDataMapper.selectDictDataBytype(dictType.getDictType());
            redisUtil.set(dictType.getDictType(), dictDataList);
        }
        return row;

    }


    /**
     * 删除系列字典类型
     * **/
    /*1.删除字典类型
     * 步骤：
     *   1.遍历传入的字典类型id
     *   2.每一个id，调selectDictById得到每一个id对应的字典类型对象
     *   3.对每一个字典类型对象的dictType字段传入到selectDictDataByDictType方法中，判断这个返回值是否大于零
     *       - 有，说明已分配类型，不可以删除，抛出自定义的异常
     *       - 无，则可以删除，进行第4步
     *   4.调用删除方法删除这个字典类型。
     *   5.删除redis中对应dictType的键值。
     *   6.上述步骤是一个id的操作，是再遍历里面执行。*/

    /*辅助方法：
    - deleteDictTypeById（只用写在mapper层在这里调用就好了
    - countDictDataByType(String dictType)*/
    /*辅助方法：deleteDictTypeById*/
    @Override
    @Transactional
    public Boolean deleteDictTypeByIds(long[] dictIds) {
        for (Long dictid : dictIds) {
            DictType dictType = dictTypeMapper.selectDictTypeById(dictid);
            if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) {
                throw new ServiceException(String.format("%1$s已经被分配，无法删除", dictType.getDictName()));
            }
            int row = dictTypeMapper.deleteDictTypeById(dictid);
            if (row > 0) {
                //redisTemplate.delete(dictType.getDictType());//这个地方我直接调用最底层的模板方法删除了，回头在rediUtil封装一下。
                redisUtil.delete(dictType.getDictType());
                return true;
            } else {
                return false;
            }
        }
        return false;
    }


    /**
     * 加载字典缓存数据
     */
    @Override
    public void loadingDictCache() {
        DictData dictData = new DictData();
        dictData.setStatus("1");
        Map<String, List<DictData>> dictDataMap = dictDataMapper.selectDictDataByCondition(dictData).stream().collect(Collectors.groupingBy(DictData::getDictType));

        /*遍历map集合分析：
         * - 1.Map.Entry<String, List<DictData>>，map接口的方内部接口，表示map集合中每一个键值对对象
         * - 2.dictDataMap.entrySet()，因为for增强遍历的对象要实现迭代器，map没有实现迭代器，所以要调用
         *   map集合对象的entrySet将所有键值对对象放到set集合里面。类似这种[{"key1", 1}、{"key2", 2}、{"key3", 3}]*/
        for (Map.Entry<String, List<DictData>> entry : dictDataMap.entrySet()) {
            redisUtil.set(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(DictData::getDictSort)).collect(Collectors.toList()));

        }
    }

    /**
     * 清除字典缓存
     */
    @Override
    public void clearDictCache() {

        // 假设字典缓存的键都以 "sys_" 开头
        String keyPattern = "sys_*";
        // 获取所有匹配的键
        Set keys = redisTemplate.keys(keyPattern);
        if (!keys.isEmpty()) {
            // 删除所有匹配的键
            redisTemplate.delete(keys);
        }
    }

    /**
     * 重置字典缓存
     */
    @Override
    public void resetDictCache()
    {
        clearDictCache();
        loadingDictCache();
    }



}
